/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.sentry;

import io.sentry.util.Objects;

import java.io.File;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
 * Sends cached events over when your App. is starting.
 *
 * @since 2021-06-02
 **/
public final class SendCachedEnvelopeFireAndForgetIntegration implements Integration {
    private final @NotNull SendFireAndForgetFactory factory;

    /**
     * SendFireAndForget
     *
     * @since 2021-06-02
     **/
    public interface SendFireAndForget {
        /**
         * send
         */
        void send();
    }

    /**
     * SendFireAndForgetDirPath
     *
     * @since 2021-06-02
     **/
    public interface SendFireAndForgetDirPath {
        /**
         * getDirPath
         *
         * @return dir path
         */
        @Nullable
        String getDirPath();
    }

    /**
     * SendFireAndForgetFactory
     *
     * @since 2021-06-02
     **/
    public interface SendFireAndForgetFactory {
        /**
         * create instance
         *
         * @param hub hub
         * @param options options
         * @return SendFireAndForget
         */
        @Nullable
        SendFireAndForget create(@NotNull IHub hub, @NotNull SentryOptions options);

        /**
         * hasValidPath
         *
         * @param dirPath dirPath
         * @param logger logger
         * @return boolean
         */
        default boolean hasValidPath(final @Nullable String dirPath, final @NotNull ILogger logger) {
            if (dirPath == null || dirPath.isEmpty()) {
                logger.log(SentryLevel.INFO, "No cached dir path is defined in options.");
                return false;
            }
            return true;
        }

        /**
         * processDir
         *
         * @param directoryProcessor directoryProcessor
         * @param dirPath dirPath
         * @param logger logger
         * @return SendFireAndForget
         */
        default @NotNull SendFireAndForget processDir(
            final @NotNull DirectoryProcessor directoryProcessor,
            final @NotNull String dirPath,
            final @NotNull ILogger logger) {
            final File dirFile = new File(dirPath);
            return () -> {
                logger.log(SentryLevel.DEBUG, "Started processing cached files from %s", dirPath);

                directoryProcessor.processDirectory(dirFile);

                logger.log(SentryLevel.DEBUG, "Finished processing cached files from %s", dirPath);
            };
        }
    }

    /**
     * constructor
     *
     * @param factory SendFireAndForgetFactory
     */
    public SendCachedEnvelopeFireAndForgetIntegration(
        final @NotNull SendFireAndForgetFactory factory) {
        this.factory = Objects.requireNonNull(factory, "SendFireAndForgetFactory is required");
    }

    @SuppressWarnings("FutureReturnValueIgnored")
    @Override
    public void register(final @NotNull IHub hub, final @NotNull SentryOptions options) {
        Objects.requireNonNull(hub, "Hub is required");
        Objects.requireNonNull(options, "SentryOptions is required");

        final String cachedDir = options.getCacheDirPath();
        if (!factory.hasValidPath(cachedDir, options.getLogger())) {
            options.getLogger().log(SentryLevel.ERROR, "No cache dir path is defined in options.");
            return;
        }

        final SendFireAndForget sender = factory.create(hub, options);

        if (sender == null) {
            options.getLogger().log(SentryLevel.ERROR, "SendFireAndForget factory is null.");
            return;
        }

        try {
            options
                .getExecutorService()
                .submit(
                    () -> {
                        try {
                            sender.send();
                        } catch (Exception e) {
                            options
                                .getLogger()
                                .log(SentryLevel.ERROR, "Failed trying to send cached events.", e);
                        }
                    });

            options
                .getLogger()
                .log(SentryLevel.DEBUG, "SendCachedEventFireAndForgetIntegration installed.");
        } catch (Exception e) {
            options
                .getLogger()
                .log(SentryLevel.ERROR, "Failed to call the executor. Cached events will not be sent", e);
        }
    }
}
